package org.phalanx.server.game;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;

import org.phalanx.map.MapCoordinates;
import org.phalanx.server.PhalanxServer;
import org.phalanx.server.ServerCache.AbstractCacheHandler;
import org.phalanx.server.ServerCache.Cache;
import org.phalanx.server.ServerCache.CacheManager;
import org.phalanx.server.ServerCache.Cacheable;
import org.phalanx.server.ServerCache.QueryObject;
import org.phalanx.server.sql.SQLAction;
import org.phalanx.server.sql.SQLManager;

public class PhalanxGameStateCacheHandler implements AbstractCacheHandler {
	public static final String tableName = "currentgamestate";

	public static final String GAMEID = "gameid";
	public static final String TURN = "turn";
	public static final String UNIT = "unit";
	public static final String COORDINATES = "coordinates";
	public static final String HIT_POINTS = "hitpoints";
	public static final String CURRENT_STATE = "currentstate";
	public static final String EXP_LEVEL = "explevel";
	public static final String FORMATION = "formation";
	public static final String USERID = "userid";
	public static final String WEALTH = "wealth";
	public static final String POSITION = "position";
	public static final String UNIT_ID = "unitid";

	@Override
	public boolean addElementToDB(Cache c, Object newKey, Cacheable newElement) {
		boolean isSuccess = false;

		SQLManager sqlMan = PhalanxServer.getServerInstance().getSQLManager();
		String[] sqlString;

		CacheManager cacheMgr = PhalanxServer.getServerInstance().getCacheManager();
		PhalanxGameSettings settings = (PhalanxGameSettings)cacheMgr.getElement("GameListCache", newKey);


		try {
			if (newElement != null && settings != null){
				Integer turnId = settings.getCurrentTurn();

				GameState gState = (GameState)newElement;

				int statementsSize = gState.getUnitStates().size();
				int count = 0;

				if (statementsSize > 0){
					sqlString = new String[statementsSize];
					Iterator<PhalanxUnitState> i = gState.getUnitStates().iterator();

					while (i.hasNext()){
						SQLAction addGameStateToDB = new SQLAction(SQLAction.INSERT, tableName);

						addGameStateToDB.addField(COORDINATES);
						addGameStateToDB.addField(CURRENT_STATE);
						addGameStateToDB.addField(EXP_LEVEL);
						addGameStateToDB.addField(FORMATION);
						addGameStateToDB.addField(GAMEID);
						addGameStateToDB.addField(HIT_POINTS);
						addGameStateToDB.addField(POSITION);
						addGameStateToDB.addField(TURN);
						addGameStateToDB.addField(UNIT);
						addGameStateToDB.addField(UNIT_ID);
						addGameStateToDB.addField(USERID);
						addGameStateToDB.addField(WEALTH);

						PhalanxUnitState uState = i.next();

						// Only add GameState to current turn
						if (uState.getTurnId().equals(turnId)){
							addGameStateToDB.addValues(convertCoordinatesToString(uState.getMapCoordinates()));
							addGameStateToDB.addValues(uState.getCurrentState());
							addGameStateToDB.addValues(uState.getExpLevel().toString());
							addGameStateToDB.addValues(uState.getFormation().toString());
							addGameStateToDB.addValues(gState.getId().toString());
							addGameStateToDB.addValues(uState.getHitPoints().toString());
							addGameStateToDB.addValues(uState.getPosition().toString());
							addGameStateToDB.addValues(uState.getTurnId().toString());
							addGameStateToDB.addValues(uState.getPhalanxUnit());
							addGameStateToDB.addValues(uState.getUnitId().toString());
							addGameStateToDB.addValues(uState.getUserId().toString());

							Integer wealth = uState.getWealth();
							String wealthStr = wealth == null ? null : wealth.toString();

							addGameStateToDB.addValues(wealthStr);

							sqlString[count++] = addGameStateToDB.getActionString();
						}
					}

					for (int x = 0; x < sqlString.length; x++){
						sqlMan.insertQuery(sqlString[x]);
					}

					isSuccess = true;
				}
			}
		} catch (SQLException se){
			se.printStackTrace();
		}


		return isSuccess;
	}

	private String convertCoordinatesToString(MapCoordinates c){
		StringBuffer mapCoorString = new StringBuffer();

		mapCoorString.append("(");
		mapCoorString.append(c.getX());
		mapCoorString.append(",");
		mapCoorString.append(c.getY());
		mapCoorString.append(")");

		return mapCoorString.toString();
	}

	@Override
	public boolean deleteObjectFromDB(Cache c, Object newKey,
			Cacheable newElement) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public ArrayList<Cacheable> getMultipleObjectsFromDB(Cache c,
			Object newKey, boolean cacheUpdate) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Cacheable getObjectFromDB(Cache c, Object newKey, boolean cacheUpdate) {
		SQLManager sqlMan = PhalanxServer.getServerInstance().getSQLManager();
		ArrayList<QueryObject> results = new ArrayList<QueryObject>();
		GameState gState = null;

		QueryObject gameStateQuery = new QueryObject();
		gameStateQuery.setInteger(GAMEID, 0);
		gameStateQuery.setInteger(TURN, 0);
		gameStateQuery.setString(UNIT, "");
		gameStateQuery.setString(COORDINATES, "");
		gameStateQuery.setInteger(HIT_POINTS, 0);
		gameStateQuery.setString(CURRENT_STATE, "");
		gameStateQuery.setInteger(EXP_LEVEL, 0);
		gameStateQuery.setInteger(FORMATION, 0);
		gameStateQuery.setInteger(USERID, 0);
		gameStateQuery.setInteger(WEALTH, 0);
		gameStateQuery.setInteger(POSITION, 0);
		gameStateQuery.setInteger(UNIT_ID, 0);

		SQLAction selectUser = new SQLAction(SQLAction.SELECT, tableName);
		selectUser.addField("*");
		selectUser.addSearchCriteriaAND(GAMEID, SQLAction.EQUALS, newKey.toString());

		try {
			results = sqlMan.getQuery(selectUser.getActionString(), gameStateQuery);

			if (!results.isEmpty()){
				Iterator<QueryObject> i = results.iterator();

				while(i.hasNext()){
					QueryObject temp = i.next();

					if (gState == null){
						Integer gameId = temp.getInteger(GAMEID);
						gState = new GameState(gameId);
					}

					PhalanxUnitState uState = new PhalanxUnitState();
					uState.setPhalanxUnit(temp.getString(UNIT));
					uState.setCurrentState(temp.getString(CURRENT_STATE));
					uState.setExpLevel(temp.getInteger(EXP_LEVEL));
					uState.setFormation(temp.getInteger(FORMATION));
					uState.setHitPoints(temp.getInteger(HIT_POINTS));
					uState.setUserId(temp.getInteger(USERID));
					uState.setWealth(temp.getInteger(WEALTH));
					uState.setPosition(temp.getInteger(POSITION));
					uState.setUnitId(temp.getInteger(UNIT_ID));
					uState.setTurnId(temp.getInteger(TURN));

					String mapCoordinatesStr = temp.getString(COORDINATES);
					if (mapCoordinatesStr.length() > 4){
						mapCoordinatesStr = mapCoordinatesStr.substring(1, mapCoordinatesStr.length() - 1);
						String[] xyCoor = mapCoordinatesStr.split(",");
						if (xyCoor.length > 1){
							MapCoordinates coorObj = new MapCoordinates(Integer.valueOf(xyCoor[0]), Integer.valueOf(xyCoor[1]));
							uState.setMapCoordinates(coorObj);

							gState.addUnitState(uState);
						}
					}
				}
				if (cacheUpdate){
					c.put(gState.getId(), gState);
				}
			}

		} catch (SQLException se){
			se.printStackTrace();
		}

		return gState;
	}

	@Override
	public boolean populateCache(Cache c) {
		boolean isSuccess = false;

		SQLManager sqlMan = PhalanxServer.getServerInstance().getSQLManager();
		ArrayList<QueryObject> results = new ArrayList<QueryObject>();

		QueryObject gameStateQuery = new QueryObject();
		gameStateQuery.setInteger(GAMEID, 0);
		gameStateQuery.setInteger(TURN, 0);
		gameStateQuery.setString(UNIT, "");
		gameStateQuery.setString(COORDINATES, "");
		gameStateQuery.setInteger(HIT_POINTS, 0);
		gameStateQuery.setString(CURRENT_STATE, "");
		gameStateQuery.setInteger(EXP_LEVEL, 0);
		gameStateQuery.setInteger(FORMATION, 0);
		gameStateQuery.setInteger(USERID, 0);
		gameStateQuery.setInteger(WEALTH, 0);
		gameStateQuery.setInteger(POSITION, 0);
		gameStateQuery.setInteger(UNIT_ID, 0);

		SQLAction getAllUsers = new SQLAction(SQLAction.SELECT, tableName);
		getAllUsers.addField("*");

		CacheManager cacheMgr = PhalanxServer.getServerInstance().getCacheManager();

		try {
			results = sqlMan.getQuery(getAllUsers.getActionString(), gameStateQuery);

			for (int i = 0; i < results.size(); i++){
				QueryObject temp = results.get(i);

				Integer gameId = temp.getInteger(GAMEID);

				PhalanxGameSettings gameSettings = (PhalanxGameSettings)cacheMgr.getElement("GameListCache", gameId);

				if (gameSettings != null){
					GameState gState;

					if (c.get(gameId) != null){
						gState = (GameState)c.get(gameId);
					}else {
						gState = new GameState(gameId);
						c.put(gState.getId(), gState);
					}

					// Grab only the last 3 turns and lazy load the others on demand
					// To keep the cache from getting too big

					Integer currentTurn = gameSettings.getCurrentTurn();
					Integer turnId = temp.getInteger(TURN);

					PhalanxUnitState uState = new PhalanxUnitState();
					uState.setPhalanxUnit(temp.getString(UNIT));
					uState.setCurrentState(temp.getString(CURRENT_STATE));
					uState.setExpLevel(temp.getInteger(EXP_LEVEL));
					uState.setFormation(temp.getInteger(FORMATION));
					uState.setHitPoints(temp.getInteger(HIT_POINTS));
					uState.setUserId(temp.getInteger(USERID));
					uState.setWealth(temp.getInteger(WEALTH));
					uState.setPosition(temp.getInteger(POSITION));
					uState.setUnitId(temp.getInteger(UNIT_ID));

					uState.setTurnId(turnId);

					String mapCoordinatesStr = temp.getString(COORDINATES);
					if (mapCoordinatesStr.length() > 4){
						mapCoordinatesStr = mapCoordinatesStr.substring(1, mapCoordinatesStr.length() - 1);
						String[] xyCoor = mapCoordinatesStr.split(",");
						if (xyCoor.length > 1){
							MapCoordinates coorObj = new MapCoordinates(Integer.valueOf(xyCoor[0]), Integer.valueOf(xyCoor[1]));
							uState.setMapCoordinates(coorObj);

							gState.addUnitState(uState);
						}
					}
				} // gamesettings != null
			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}


		return isSuccess;
	}

	@Override
	public boolean updateElementToDB(Cache c, Object newKey,
			Cacheable newElement) {

		// Reference game list's current turn to know which items to update
		// So as to not have to constantly update every single turn on every move.
		SQLManager sqlMan = PhalanxServer.getServerInstance().getSQLManager();

		Integer gameId = (Integer)newKey;
		GameState gState = (GameState)newElement;
		CacheManager cacheMgr = PhalanxServer.getServerInstance().getCacheManager();

		PhalanxGameSettings settings = (PhalanxGameSettings)cacheMgr.getElement("GameListCache", gameId);

		boolean isSuccess = false;

		if (settings != null && gState != null){
			Integer turnId = settings.getCurrentTurn();

			Iterator<PhalanxUnitState> i = gState.getUnitStates().iterator();

			while(i.hasNext()){
				PhalanxUnitState uState = i.next();

				if (uState.getTurnId().equals(turnId)){
					SQLAction updateGameStateToDB = new SQLAction(SQLAction.INSERT, tableName);

					updateGameStateToDB.addField(COORDINATES);
					updateGameStateToDB.addField(CURRENT_STATE);
					updateGameStateToDB.addField(EXP_LEVEL);
					updateGameStateToDB.addField(FORMATION);
					updateGameStateToDB.addField(GAMEID);
					updateGameStateToDB.addField(HIT_POINTS);
					updateGameStateToDB.addField(POSITION);
					updateGameStateToDB.addField(TURN);
					updateGameStateToDB.addField(UNIT);
					updateGameStateToDB.addField(UNIT_ID);
					updateGameStateToDB.addField(USERID);
					updateGameStateToDB.addField(WEALTH);

					updateGameStateToDB.addValues(convertCoordinatesToString(uState.getMapCoordinates()));
					updateGameStateToDB.addValues(uState.getCurrentState());
					updateGameStateToDB.addValues(uState.getExpLevel().toString());
					updateGameStateToDB.addValues(uState.getFormation().toString());
					updateGameStateToDB.addValues(gState.getId().toString());
					updateGameStateToDB.addValues(uState.getHitPoints().toString());
					updateGameStateToDB.addValues(uState.getPosition().toString());
					updateGameStateToDB.addValues(uState.getTurnId().toString());
					updateGameStateToDB.addValues(uState.getPhalanxUnit());
					updateGameStateToDB.addValues(uState.getUnitId().toString());
					updateGameStateToDB.addValues(uState.getUserId().toString());
					updateGameStateToDB.addValues(uState.getWealth() == null ? null : uState.getWealth().toString());

					updateGameStateToDB.addSearchCriteriaAND(GAMEID, SQLAction.EQUALS, gState.getId().toString());
					updateGameStateToDB.addSearchCriteriaAND(TURN, SQLAction.EQUALS, turnId.toString());

					try {
						sqlMan.insertQuery(updateGameStateToDB.getActionString());
						isSuccess  = true;
					} catch (SQLException se){
						se.printStackTrace();
						isSuccess = false;
					}
				}
			}
		}

		return isSuccess;
	}

}
